/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package se.kth.lwr.jeat.core;

import java.util.List;

/**
 * This class is an aggregate of different DataSources which also describes their
 * relations. Basically it express a set of mathematical operations on 
 * a {@code DataSource<Inventoriable>}. Thus the output of this 
 * function will in the end be of type {@link Inventoriable}. Various operations 
 * that are allowed for an {@link Inventory} can be performed also with 
 * DataSources in this class. However, all arguments have to be DataSources.
 *<p>
 * The relation between the DataSource arguments are recorded. Thus, the output
 * of the function could be derived and reported
 * </p>
 * @author davidev
 */
//TODO Custom report building for this class
public class InventoryFunction extends SourceFunction<Inventoriable> {
    

    static String name = "InventoryFunction";
    static String description = "A source function that operates on an Inventory";

    /**
     * Creates an InventoryFunction with an initial value
     *
     * @param initialValue Initial value of the function
     */
    public InventoryFunction(InventoryDataSource initialValue) {
        super(name, description, initialValue);
    }
    
//    /**
//     * 
//     * @return 
//     */
//    @Override
//    public Report getReport() {
//        Report retval= super.getReport();
//        List<Report.ReportRecord> valuerecords=readValue().getInventory().getReport().getReportRecords();
//        retval.addRows(valuerecords);
//        return retval;
//    }
    
    

    /**
     * Creates an InventoryFunction with an empty inventory as initial value
     */
    public InventoryFunction() {
        super(name, description, Inventory.emptyInventory().asInventoriable());
    }

    /**
     * merges the value of the function with the argument
     *
     * @param inventoryDataSource the argument
     * @return the resulting function
     */
    public InventoryFunction merge(InventoryDataSource dataSource) {
        recordOperation(new MergeOperator(dataSource));
        return this;
    }

    /**
     * Multiply the value of the function with the argument
     *
     * @param dataSource the multiplier
     * @return the resulting function
     */
    public InventoryFunction multiply(AmountDataSource dataSource) {
        recordOperation(new MultiplyOperator(dataSource));
        return this;
    }

    /**
     * <p> Returns the difference (D) between the function (X) and the argument
     * (Y) </p> D=X-Y
     *
     * @param dataSource the argument
     * @return the resulting function
     */
    public InventoryFunction differenceTo(InventoryDataSource dataSource) {
        recordOperation(new DifferenceToOperator(dataSource));
        return this;
    }

    /**
     * An Operator that define a multiplication with an AmountDataSource
     */
    class MultiplyOperator extends Operator<Inventoriable, AmountDataSource> {

        public MultiplyOperator(AmountDataSource operatorValue) {
            super(operatorValue);
        }

        @Override
        Inventoriable operate(Inventoriable operand, Activity dataUser) {
            Inventory func = operand.getInventory();
            func = func.multiply(getArgument().useValue(dataUser));
            return func.asInventoriable();
        }

        @Override
        String getOperationSign() {
            return "*";
        }

        @Override
        Inventoriable operate(Inventoriable operand) {
            Inventory func = operand.getInventory();
            func = func.multiply(getArgument().readValue());
            return func.asInventoriable();
        }
    }

    /**
     * An operator that defines a merge with an InventoryDataSource
     */
    class MergeOperator extends Operator<Inventoriable, InventoryDataSource> {

        public MergeOperator(InventoryDataSource operatorValue) {
            super(operatorValue);
        }

        @Override
        Inventoriable operate(Inventoriable operand, Activity dataUser) {
            Inventory func = operand.getInventory();
            func = func.merge(getArgument().useValue(dataUser).getInventory());
            return func.asInventoriable();
        }

        @Override
        String getOperationSign() {
            return "+";
        }

        @Override
        Inventoriable operate(Inventoriable operand) {
            Inventory func = operand.getInventory();
            func = func.merge(getArgument().readValue().getInventory());
            return func.asInventoriable();
        }
    }

    /**
     * An operator that defines the difference operation to an
     * InventoryDataSource
     */
    class DifferenceToOperator extends Operator<Inventoriable, InventoryDataSource> {

        public DifferenceToOperator(InventoryDataSource operatorValue) {
            super(operatorValue);
        }

        @Override
        Inventoriable operate(Inventoriable operand, Activity dataUser) {
            Inventory func = operand.getInventory();
            func = func.difference(getArgument().useValue(dataUser).getInventory());
            return func.asInventoriable();
        }

        @Override
        String getOperationSign() {
            return "-";
        }

        @Override
        Inventoriable operate(Inventoriable operand) {
            Inventory func = operand.getInventory();
            func = func.difference(getArgument().readValue().getInventory());
            return func.asInventoriable();
        }
    }
}
